package tianrun.ziguan.api.calculate.config.service.sync.handler.template;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import tianrun.ziguan.api.calculate.config.dto.request.*;
import tianrun.ziguan.api.calculate.config.dto.request.common.DeleteBody;
import tianrun.ziguan.api.calculate.config.dto.response.TAssetCalLabelConfigVO;
import tianrun.ziguan.api.calculate.config.entity.*;
import tianrun.ziguan.api.calculate.config.enums.MoldEnum;
import tianrun.ziguan.api.calculate.config.service.cal.label.ITCalLabelConfigService;
import tianrun.ziguan.api.calculate.config.service.common.CommonService;
import tianrun.ziguan.api.calculate.config.service.expression.ITExpressionService;
import tianrun.ziguan.api.calculate.config.service.sync.SyncType;
import tianrun.ziguan.api.calculate.config.service.sync.handler.AbstractSyncHandler;
import tianrun.ziguan.api.calculate.config.service.sync.handler.SyncHandler;
import tianrun.ziguan.api.calculate.config.service.template.ITTemplateService;
import tianrun.ziguan.api.calculate.config.service.template.instance.ITIndicatorsInstanceService;
import tianrun.ziguan.api.calculate.config.service.template.module.ITIndicatorsModuleService;
import tianrun.ziguan.api.calculate.config.utils.CollectionUtil;
import tianrun.ziguan.api.calculate.config.utils.MyBeanUtil;
import tianrun.ziguan.api.calculate.config.utils.Setter;
import tianrun.ziguan.api.common.util.ListUtil;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class IndicatorsModuleHandler extends AbstractSyncHandler {

    private final ITIndicatorsModuleService indicatorsModuleService;
    private final ITIndicatorsInstanceService indicatorsInstanceService;
    private final ITExpressionService expressionService;
    private final ITCalLabelConfigService calLabelConfigService;
    private final CommonService commonService;
    private final ITTemplateService templateService;
    private final String instanceTableName = "t_indicators_instance";

    public IndicatorsModuleHandler(ITIndicatorsModuleService indicatorsModuleService, ITIndicatorsInstanceService indicatorsInstanceService, ITExpressionService expressionService, ITCalLabelConfigService calLabelConfigService, CommonService commonService, ITTemplateService templateService) {
        this.indicatorsModuleService = indicatorsModuleService;
        this.indicatorsInstanceService = indicatorsInstanceService;
        this.expressionService = expressionService;
        this.calLabelConfigService = calLabelConfigService;
        this.commonService = commonService;
        this.templateService = templateService;
    }

    @Override
    public MoldEnum mold() {
        return MoldEnum.CustomMeasure;
    }

    @Override
    public SyncType type() {
        return SyncType.TEMPLATE;
    }

    @Override
    public void handler(Set<Long> moduleIds, Set<Long> parentIds) {
        Map<TIndicatorsModule, List<TIndicatorsInstance>> map = new HashMap<>();
        List<TTemplate> templates = templateService.getByIds(parentIds);
        //1获取模板下的所有组件和实例
        List<TIndicatorsModule> sourceModules = indicatorsModuleService.getByIds(moduleIds);
        List<TIndicatorsInstance> sourceInstances = indicatorsInstanceService.listByModuleIds(moduleIds);
        List<CalLabelConfigVO> sourceLabelConfigs = calLabelConfigService.listVOByModuleIds(moduleIds, instanceTableName);
        List<ExpressionVO> sourceExpressions = expressionService.listVOByModuleIds(moduleIds, instanceTableName);

        Map<TIndicatorsModule, List<TIndicatorsInstance>> sourceMap = sourceModules.stream().collect(Collectors.toMap(Function.identity(), module -> sourceInstances.stream().filter(i -> Objects.equals(i.getModuleId(), module.getId())).collect(Collectors.toList())));
        Map<Long, TIndicatorsInstance> sourceInstanceMap = sourceInstances.stream().collect(Collectors.toMap(TIndicatorsInstance::getId, Function.identity()));
        //2获取对应的资产下的组件和实例
        List<TIndicatorsModule> indicatorsModules = indicatorsModuleService.getByTemplate(parentIds);
        Collection<IndicatorsInstanceVO> instanceVOS = indicatorsInstanceService.listVOByTemplateIds(parentIds);
        Map<Long, List<IndicatorsInstanceVO>> templateIdInstancesMap = ListUtil.groupList(instanceVOS, IndicatorsInstanceVO::getTemplateId);

        //获取config
        List<CalLabelConfigVO> assetCalLabelConfigs = calLabelConfigService.listVOByTemplateIds(parentIds, instanceTableName);

        //获取表达式
        List<ExpressionVO> assetExpressions = expressionService.listVOByTemplateIds(parentIds, instanceTableName);
        Map<Long, List<TIndicatorsModule>> targetMap = new HashMap<>();
        //初始化
        templates.forEach(asset -> targetMap.put(asset.getId(), Lists.newArrayList()));
        targetMap.putAll(ListUtil.groupList(indicatorsModules, TIndicatorsModule::getTemplateId));


        //对比-组件和实例的关联字段
        targetMap.forEach((templateId, targetModules) -> {
            sourceMap.forEach((sourceModule, sourceInstanceList) -> {
                Optional<TIndicatorsModule> first = targetModules.stream().filter(m -> Objects.equals(m.getName(), sourceModule.getName())).findFirst();
                TIndicatorsModule newTargetModule = first.orElseGet(TIndicatorsModule::new);
                MyBeanUtil.doCopy(sourceModule, newTargetModule,
                        Setter.set(newTargetModule::setId, newTargetModule.getId()),
                        Setter.set(newTargetModule::setTemplateId, templateId)
                        );
                List<TIndicatorsInstance> collect = templateIdInstancesMap.getOrDefault(templateId, Lists.newArrayList()).stream().map(v -> (TIndicatorsInstance) v).collect(Collectors.toList());
                map.put(newTargetModule, getNewTargetTemplateInstances(collect, sourceInstanceList, newTargetModule, TIndicatorsInstance.class));

            });
        });
        if (map.isEmpty()) return;

        //修改数据
        indicatorsModuleService.saveOrUpdateBatch(map.keySet());
        map.forEach((module, instances) -> instances.forEach(instance -> instance.setModuleId(module.getId())));
        List<TIndicatorsInstance> saveInstances = map.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
        if (saveInstances.isEmpty()) return;
        indicatorsInstanceService.saveOrUpdateBatch(saveInstances);

        //表达式和虚拟变量通用操作
        labelConfigAndExpressionHandleByTemplate(sourceLabelConfigs, sourceExpressions, assetCalLabelConfigs, assetExpressions, saveInstances, instanceTableName, expressionService, calLabelConfigService);
        // TODO: 2023/11/4 是否删除组件内的其它实例? 删除解除下面注释就行
        //删除组件内其它实例数据,级联删除
        /*
        List<Long> instanceIds = saveInstances.stream().map(TIndicatorsInstance::getId).collect(Collectors.toList());
        List<TIndicatorsInstance> delInstances = indicatorsInstanceService.list(Wrappers.
                <TIndicatorsInstance>lambdaQuery().
                in(TIndicatorsInstance::getModuleId, map.keySet().stream().map(TIndicatorsModule::getId).collect(Collectors.toList())).
                notIn(TIndicatorsInstance::getId, instanceIds)
        );
        if (!delInstances.isEmpty()) {
            commonService.delete(DeleteBody.of(delInstances.stream().map(TAssetIndicatorsInstance::getId).collect(Collectors.toSet()), instanceTableName));
        }*/
    }

    //@Override
    public void handler1(Set<Long> moduleIds, Set<Long> parentIds) {
        Map<Long, Map<Long, TIndicatorsModule>> modules = getModules(moduleIds, parentIds);
        Collection<TIndicatorsInstance> sources = indicatorsInstanceService.listByModuleIds(moduleIds);
        Collection<IndicatorsInstanceVO> targets = indicatorsInstanceService.listVOByTemplateIds(parentIds);
        Map<Long, List<IndicatorsInstanceVO>> templateMapping = targets.stream().collect(Collectors.groupingBy(IndicatorsInstanceVO::getTemplateId));
        List<TIndicatorsInstance> list = Lists.newArrayList();
        List<Long> delIds = Lists.newArrayList();
        for (TIndicatorsInstance source : sources) {
            String key = source.getCalLabel();
            Map<Long, TIndicatorsModule> moduleMap = modules.get(source.getModuleId());
            moduleMap.forEach((parentId, module) -> {
                List<IndicatorsInstanceVO> values = templateMapping.getOrDefault(parentId, Collections.emptyList());
                List<Long> ids = values.stream().filter(i -> Objects.equals(i.getCalLabel(), key)).map(IndicatorsInstanceVO::getId).collect(Collectors.toList());
                delIds.addAll(ids);
                IndicatorsInstanceVO target = new IndicatorsInstanceVO();
                list.add(MyBeanUtil.doCopy(source, target, new Setter<>(target::setId, null), new Setter<>(target::setModuleId, module.getId())));
            });
        }
        if (list.isEmpty()) return;
        indicatorsInstanceService.saveOrUpdateBatch(list);
        if (!delIds.isEmpty()) {
            indicatorsInstanceService.removeByIds(delIds);
        }
        Set<Long> sourceInstanceIds = CollectionUtil.toSet(sources, TIndicatorsInstance::getId);
        Set<Long> targetInstanceIds = CollectionUtil.toSet(list, TIndicatorsInstance::getId);
        this.handlerExpression(sourceInstanceIds, targetInstanceIds);
        this.handlerCalLabelConfig(sourceInstanceIds, targetInstanceIds);
    }

    private Map<Long, Map<Long, TIndicatorsModule>> getModules(Set<Long> moduleIds, Set<Long> templateIds) {
        Map<Long, Map<Long, TIndicatorsModule>> map = Maps.newHashMap();
        List<TIndicatorsModule> list = Lists.newArrayList();
        for (Long moduleId : moduleIds) {
            TIndicatorsModule module = indicatorsModuleService.getById(moduleId);
            module.setId(null);
            Map<Long, TIndicatorsModule> map1 = Maps.newHashMap();
            for (Long templateId : templateIds) {
                TIndicatorsModule module1 = new TIndicatorsModule();
                BeanUtils.copyProperties(module, module1);
                module1.setName(module.getName() + StringPool.UNDERSCORE + System.currentTimeMillis());
                module1.setTemplateId(templateId);
                map1.put(templateId, module1);
                list.add(module1);
            }
            map.put(moduleId, map1);
        }
        indicatorsModuleService.saveBatch(list);
        return map;
    }

    private void handlerExpression(Set<Long> sourceInstanceIds, Set<Long> targetInstanceIds) {
        List<TExpression> sources = expressionService.listByInstanceIds(sourceInstanceIds, instanceTableName);
        List<TExpression> targets = expressionService.querySyncListByInstanceIds(targetInstanceIds, instanceTableName);
        Map<String, TExpression> sourceMapping = CollectionUtil.toMap(sources, TExpression::getCalLabel);
        Map<String, List<TExpression>> targetMapping = CollectionUtil.groupingBy(targets, TExpression::getCalLabel);
        List<TExpression> list = Lists.newArrayList();
        sourceMapping.forEach((calLabel, source) -> {
            List<TExpression> _targets = targetMapping.get(calLabel);
            List<TExpression> _list = _targets.stream().map(target -> MyBeanUtil.doCopy(source, target, new Setter<>(target::setId, target.getId()), new Setter<>(target::setRelationId, target.getRelationId()))).collect(Collectors.toList());
            list.addAll(_list);
        });
        if (!list.isEmpty()) {
            expressionService.saveOrUpdateBatch(list);
        }
    }

    private void handlerCalLabelConfig(Set<Long> sourceInstanceIds, Set<Long> targetInstanceIds) {
        List<TCalLabelConfig> sources = calLabelConfigService.listByInstanceIds(sourceInstanceIds, instanceTableName);
        List<TCalLabelConfig> targets = calLabelConfigService.querySyncListByInstanceIds(targetInstanceIds, instanceTableName);
        Map<String, List<TCalLabelConfig>> sourceMapping = CollectionUtil.groupingBy(sources, TCalLabelConfig::getCalLabel);
        Map<String, List<TCalLabelConfig>> targetMapping = CollectionUtil.groupingBy(targets, TCalLabelConfig::getCalLabel);
        List<TCalLabelConfig> list = Lists.newArrayList();
        List<Long> delIds = Lists.newArrayList();
        sourceMapping.forEach((calLabel, _sources) -> {
            List<TCalLabelConfig> _targets = targetMapping.get(calLabel);
            for (TCalLabelConfig target : _targets) {
                Optional<TCalLabelConfig> optional = _sources.stream().filter(item -> item.getLabel().equals(target.getLabel())).findFirst();
                if (optional.isPresent()) {
                    TCalLabelConfig source = optional.get();
                    list.add(MyBeanUtil.doCopy(source, target, new Setter<>(target::setId, target.getId()), new Setter<>(target::setRelationId, target.getRelationId())));
                } else {
                    delIds.add(target.getId());
                }
            }
        });
        if (!list.isEmpty()) {
            calLabelConfigService.saveOrUpdateBatch(list);
        }
        if (!delIds.isEmpty()) {
            calLabelConfigService.removeByIds(delIds);
        }

    }

}
